﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using OmniKits.Core;
using System.Collections;
using System.Threading;

namespace OmniKits.NanaTube
{
    public partial class NanaTubeClassic<T>
        where T : class
    {
        // Here comes MAGIC! DO NOT TOUCH if you have no clue about memory model and/or volatile read/write

        private class Holder
          : DisposableBase
        {
            // 由于任何其它线程都仅在这些成员被初始化后对其进行一次性读取，因此并不需要 volatile 约束
            public readonly T Data;
            private Holder _Next;

            // 应用 volatile 修饰符以避免以下可能出现的问题: 
            // - 向内存中写入不完整状态
            // - 从寄存器或缓存中读取而未及时了解最新状态
            private volatile ManualResetEvent _Event = new ManualResetEvent(false);

            public Holder(T data)
            {
                // 使用 Interlocked.Exchange 的目的是为了避免向内存中写入不完整的状态
                Interlocked.Exchange(ref Data, data);
            }

            public Holder GetNext()
            {
                this.Wait();
                return _Next;
            }

            public void SetNext(Holder next)
            {
                //if (null == next)
                //    return;

                // 其它线程仅能在 this.Set 被调用之后方可读取 _Next 的内容，而这个方法内部包含了具有内存壁障作用的代码
                _Next = next;

                this.Set();
            }

            private void Set()
            {
                this.Dispose();
            }

            public void Wait()
            {
                // 永远直接从内存中提取最新状态, 目前依赖于 volatile 修饰符的作用
                var @event = _Event;
                if (null == @event)
                    return;

                try
                {
                    @event.WaitOne();
                }
                catch (ObjectDisposedException)
                {
                    // 由于 WaitOne 方法会检查事件对象的状态并可能阻止对底层 API 的调用，故在此插入内存壁障以确保观察到正确的状态
                    // HACK: 有可能在读取 _Event 到调用 WaitOne 方法之间发生 _Event 被销毁的情况, 此时缓存状态不明
                    Thread.MemoryBarrier();
                }
            }

            #region 消解

            protected override void Dispose(bool disposing)
            {
                var @event = _Event;
                _Event = null;

                // 无论此对象出于何种原因被消解，都必须在此处对事件放行并消解、置空。
                @event.Set();
                @event.Dispose();
            }

            public void Dispose()
            {
                ((IDisposable)this).Dispose();
            }
            
            #endregion
        }
    }
}
